home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / FastWriteStream.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-22  |  10.9 KB  |  466 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "VirtualDub.h"
  19.  
  20. #include <process.h>
  21.  
  22. #include <windows.h>
  23.  
  24. #include "Error.h"
  25.  
  26. #include "FastWriteStream.h"
  27. #include "tls.h"
  28.  
  29. extern CRITICAL_SECTION g_diskcs;
  30. extern bool g_disklockinited;
  31.  
  32. //////////////////////////////////////////////////////////////////////
  33.  
  34. FastWriteStream::FastWriteStream(const char *lpszFile, long lBufferSize, long lChunkSize, bool fLaunchThread) {
  35.  
  36.     hFile = hFileClose = CreateFile(
  37.             lpszFile,
  38.             GENERIC_READ | GENERIC_WRITE,
  39.             FILE_SHARE_WRITE,
  40.             NULL,
  41.             OPEN_ALWAYS,
  42.             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
  43.             NULL
  44.         );
  45.  
  46.     if (INVALID_HANDLE_VALUE == hFile) {
  47.  
  48.         // ARGH.  Attempt open without FILE_FLAG_NO_BUFFERING
  49.  
  50.         hFile = hFileClose = CreateFile(
  51.                 lpszFile,
  52.                 GENERIC_READ | GENERIC_WRITE,
  53.                 FILE_SHARE_WRITE,
  54.                 NULL,
  55.                 OPEN_ALWAYS,
  56.                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_SEQUENTIAL_SCAN,
  57.                 NULL
  58.             );
  59.  
  60.         if (INVALID_HANDLE_VALUE == hFile)
  61.             throw MyError("FastWriteStream: open failed");
  62.     }
  63.  
  64.     this->lBufferSize        = lBufferSize;
  65.     this->lChunkSize        = lChunkSize;
  66.  
  67.     _construct(fLaunchThread);
  68. }
  69.  
  70. FastWriteStream::FastWriteStream(HANDLE hFile, long lBufferSize, long lChunkSize, bool fLaunchThread) {
  71.     this->hFile                = hFile;
  72.     this->lBufferSize        = lBufferSize;
  73.     this->lChunkSize        = lChunkSize;
  74.     this->hFileClose        = NULL;
  75.  
  76.     _construct(fLaunchThread);
  77. }
  78.  
  79. void FastWriteStream::_construct(bool fLaunchThread) {
  80.     unsigned uiThreadId;
  81.  
  82.     lReadPointer            = 0;
  83.     lWritePointer            = 0;
  84.     lDataPoint                = 0;
  85.     lpBuffer                = NULL;
  86.  
  87.     hEventOkRead            = NULL;
  88.     hEventOkWrite            = NULL;
  89.     hThread                    = NULL;
  90.  
  91.     dwErrorRet                = 0;
  92.     fDie                    = FALSE;
  93.     fFlush                    = FALSE;
  94.     fSynchronous            = false;
  95.  
  96.     try {
  97.         // #1:    lChunkSize *must* be a multiple of the disk sector size.
  98.         //
  99.         //        There isn't a reliable way to determine the disk sector size,
  100.         //        because GetDiskFreeSpace() lies for disks >4Gb (FAT32).  So
  101.         //        we're just going to assume a 2K sector size.
  102.  
  103.         lChunkSize = (lChunkSize + 2047) & -2048;
  104.  
  105.         // #2:    lBufferSize *must* be a multiple of lChunkSize.
  106.         //
  107.         //        We partition our buffer into a number of chunks, so that we
  108.         //        never have to write subchunks when the buffer wraps, which
  109.         //        could seriously hurt performance.  So let's round up the
  110.         //        buffer size to an integral number of chunks.
  111.  
  112.         lBufferSize += lChunkSize - 1;
  113.  
  114.         lBufferSize -= lBufferSize % lChunkSize;
  115.  
  116.         // Allocate the buffer.  VirtualAlloc() guarantees that the buffer will
  117.         // be aligned to the nearest page boundary, which is 4K or 4Mb on Intel
  118.         // CPUs.
  119.  
  120.         lpBuffer = VirtualAlloc(NULL, lBufferSize, MEM_COMMIT, PAGE_READWRITE);
  121.  
  122.         if (!lpBuffer) throw MyMemoryError();
  123.  
  124.         memset(lpBuffer, 0, lBufferSize);
  125.  
  126.         // Allocate events.
  127.  
  128.         if (    !(hEventOkRead  = CreateEvent(NULL, FALSE, FALSE, NULL))
  129.             ||    !(hEventOkWrite = CreateEvent(NULL, FALSE, FALSE, NULL)))
  130.  
  131.             throw MyMemoryError();
  132.  
  133.     if (!g_disklockinited) {
  134.         g_disklockinited=true;
  135.         InitializeCriticalSection(&g_diskcs);
  136.     }
  137.         // Launch background I/O thread if necessary
  138.  
  139.         if (fLaunchThread)
  140.             if (!(hThread = (HANDLE)_beginthreadex(NULL, 0, FastWriteStream::BackgroundThreadStart, (void *)this, 0, &uiThreadId)))
  141.                 throw MyError("FastWriteStream: couldn't start background I/O thread");
  142.  
  143.         // All done!
  144.  
  145.     } catch(...) {
  146.         _destruct();
  147.         throw;
  148.     }
  149. }
  150.  
  151. void FastWriteStream::_destruct() {
  152.     _RPT0(0,"FastWriteStream::_destruct()\n");
  153.  
  154.     if (hThread) {
  155.         fDie = TRUE;
  156.         SetEvent(hEventOkRead);
  157.         WaitForSingleObject(hThread, INFINITE);
  158.         CloseHandle(hThread);
  159.     }
  160.     if (hEventOkRead) { CloseHandle(hEventOkRead); hEventOkRead = NULL; }
  161.     if (hEventOkWrite) { CloseHandle(hEventOkWrite); hEventOkWrite = NULL; }
  162.     if (lpBuffer) {
  163.         VirtualFree(lpBuffer, 0, MEM_RELEASE);
  164.         lpBuffer = NULL;
  165.     }
  166.     if (hFileClose && hFileClose != INVALID_HANDLE_VALUE) {
  167.         CloseHandle(hFileClose);
  168.         hFileClose = NULL;
  169.     }
  170. }
  171.  
  172. FastWriteStream::~FastWriteStream() {
  173.     _destruct();
  174. }
  175.  
  176. //////////////////////////////////////////////////////////////////////
  177.  
  178. void FastWriteStream::ThrowError() {
  179. //    throw MyError("FastWriteStream: write error %08lx\n", dwErrorRet);
  180.     throw MyWin32Error("FastWriteStream: %%s\n", dwErrorRet);
  181. }
  182.  
  183. #pragma function(memcpy)
  184.  
  185. void FastWriteStream::_Put(void *data, long len) {
  186.  
  187.     if (dwErrorRet) ThrowError();
  188.  
  189.     while(len > 0) {
  190.         long buffree;
  191.  
  192.         while((buffree = lBufferSize - lDataPoint)<=0) {
  193.             if (fSynchronous)
  194.                 BackgroundCheck();
  195.             else
  196.                 WaitForSingleObject(hEventOkWrite, INFINITE);
  197.  
  198.             if (dwErrorRet) ThrowError();
  199.         }
  200.  
  201.         if (lWritePointer + buffree > lBufferSize)
  202.             buffree = lBufferSize - lWritePointer;
  203.  
  204.         if (buffree > len)
  205.             buffree = len;
  206.  
  207.         memcpy((char *)lpBuffer + lWritePointer, data, buffree);
  208.  
  209.         len -= buffree;
  210.         data = (char *)data + buffree;
  211.         lWritePointer += buffree;
  212.  
  213.         if (lWritePointer >= lBufferSize)
  214.             lWritePointer -= lBufferSize;
  215.  
  216.         // atomic add
  217.  
  218.         __asm mov eax,this
  219.         __asm mov ebx,buffree
  220.         __asm lock add [eax]FastWriteStream.lDataPoint,ebx
  221.  
  222.         // Signal the background thread if there might be enough to write.
  223.         //
  224.         // There's a chance that the background thread flips in here and
  225.         // takes out the data before we get to this point.  But if that
  226.         // happens, well, it just did what we wanted it to anyway, so why
  227.         // bother signalling it?
  228.  
  229.         if (lDataPoint >= lChunkSize)
  230.             SetEvent(hEventOkRead);
  231.     }
  232. }
  233.  
  234. void FastWriteStream::Put(void *lpData, long len) {
  235.     _Put(lpData, len);
  236. }
  237.  
  238. void FastWriteStream::Putc(char c) {
  239.     Put(&c, sizeof c);
  240. }
  241.  
  242. void FastWriteStream::Putl(long l) {
  243.     Put(&l, sizeof l);
  244. }
  245.  
  246. long FastWriteStream::Flush1() {
  247.  
  248.     if (dwErrorRet) ThrowError();
  249.  
  250.     while(lDataPoint >= lChunkSize) {
  251.  
  252.         if (fSynchronous)
  253.             BackgroundCheck();
  254.         else
  255.             WaitForSingleObject(hEventOkWrite, INFINITE);
  256.  
  257.         if (dwErrorRet) ThrowError();
  258.  
  259.     }
  260.  
  261.     return lDataPoint;
  262. }
  263.  
  264. void FastWriteStream::Flush2(HANDLE hFileSlow) {
  265.     fFlush = TRUE;
  266.  
  267.     this->hFileSlow = hFileSlow;
  268.  
  269.     if (dwErrorRet) ThrowError();
  270.  
  271.     while(lDataPoint > 0) {
  272.  
  273.         SetEvent(hEventOkRead);
  274.  
  275.         if (fSynchronous)
  276.             BackgroundCheck();
  277.         else
  278.             WaitForSingleObject(hEventOkWrite, INFINITE);
  279.  
  280.         if (dwErrorRet) ThrowError();
  281.  
  282.     }
  283. }
  284.  
  285. void FastWriteStream::FlushStart() {
  286.     FlushFileBuffers(hFile);
  287. }
  288.  
  289. void FastWriteStream::Seek(__int64 llPos) {
  290.     LONG lLow, lHigh;
  291.  
  292.     lLow    = (LONG)llPos;
  293.     lHigh    = (LONG)(llPos >> 32);
  294.  
  295.     // make sure output thread is finished
  296.  
  297.     Flush1();
  298.  
  299.     SetLastError(0);
  300.  
  301. //    _RPT2(0,"FastWriteStream::Seek(%08lx%08lx)\n", lHigh, lLow);
  302.  
  303.     SetFilePointer(hFile, lLow, &lHigh, FILE_BEGIN);
  304.  
  305.     if (dwErrorRet = GetLastError())
  306.         ThrowError();
  307.  
  308. //    _RPT0(0,"FastWriteStream::Seek() exit\n");
  309. }
  310.  
  311. long FastWriteStream::getBufferStatus(long *lplBufferSize) {
  312.     *lplBufferSize = lBufferSize;
  313.  
  314.     return lBufferSize - lDataPoint;
  315. }
  316.  
  317. //////////////////////////////////////////////////////////////////////
  318.  
  319. void FastWriteStream::putError(DWORD dwErrorRet) {
  320.     this->dwErrorRet = dwErrorRet;
  321. }
  322.  
  323. unsigned __stdcall FastWriteStream::BackgroundThreadStart(void *lpThisPtr) {
  324.     FastWriteStream *thisPtr = (FastWriteStream *)lpThisPtr;
  325.  
  326.     InitThreadData("FastWriteStream");
  327.  
  328.     _RPT2(0,"FastWriteStream thread start: thread=%p, this=%p\n", GetCurrentThreadId(), thisPtr);
  329.  
  330.     try {
  331.  
  332.         thisPtr->BackgroundThread();
  333.  
  334.     } catch(DWORD dwError) {
  335.         thisPtr->putError(dwError);
  336.     }
  337.  
  338.     while(!thisPtr->fDie) {
  339.         WaitForSingleObject(thisPtr->hEventOkRead, INFINITE);
  340.  
  341.         SetEvent(thisPtr->hEventOkWrite);
  342.     }
  343.  
  344.     DeinitThreadData();
  345.  
  346.     return 0;
  347. }
  348.  
  349. void FastWriteStream::BackgroundWrite(HANDLE hFile, long lOffset, long lSize) {
  350.     DWORD dwActual;
  351.  
  352.     EnterCriticalSection(&g_diskcs);
  353.     if (!WriteFile(hFile,(char *)lpBuffer + lOffset,lSize,&dwActual,NULL)) {
  354.         LeaveCriticalSection(&g_diskcs);
  355.         throw GetLastError();
  356.     }
  357.         LeaveCriticalSection(&g_diskcs);
  358.  
  359.     if (dwActual != lSize)
  360.         throw ERROR_WRITE_FAULT;
  361. }
  362.  
  363. bool FastWriteStream::BackgroundCheck() {
  364.     // If we can, write an integral number of chunks to disk
  365.  
  366.     if ((lDataPoint < lChunkSize) && (!fFlush || !lDataPoint))
  367.         return false;
  368.  
  369.     if (dwErrorRet) {
  370.         SetEvent(hEventOkWrite);
  371.         return true;
  372.     }
  373.  
  374.     long len;
  375.     HANDLE hFOut = hFile;
  376.  
  377.     // Our minimal write will be one chunk's size.  However,
  378.     // if there's more data in the buffer (we're playing catch-up),
  379.     // then we may need to split the write, although the minimal
  380.     // size will be at least 1 chunk and still be aligned.
  381.     //
  382.     // The exception is if a flush is asked.  When this happens,
  383.     // a split write may still be required, but the last write will
  384.     // be sector-aligned if the user sector-aligned the data.
  385.  
  386.     if (fFlush) {
  387.         len = lDataPoint;
  388.  
  389. #ifdef _DEBUG
  390.         if (len && len < 2048)
  391.             _RPT0(0,"FastWriteStream: Error! Final flushed write is not a 2K multiple!\n");
  392. #endif
  393.         if (len >= 2048)
  394.             len -= len % 2048;
  395.  
  396.     } else {
  397.         len = lDataPoint - lDataPoint % lChunkSize;
  398.  
  399.         if (len > lChunkSize)
  400.             len = lChunkSize;
  401.     }
  402.  
  403.     if (len < 2048) {
  404.         LONG dwLow, dwHigh = 0;
  405.         DWORD dwErrorRet;
  406.  
  407.         // Determine fast path location
  408.  
  409.         SetLastError(0);
  410.         dwLow = SetFilePointer(hFile, 0, &dwHigh, FILE_CURRENT);
  411.  
  412.         if (dwErrorRet = GetLastError())
  413.             throw dwErrorRet;
  414.  
  415.         // Seek slow path up
  416.  
  417.         SetFilePointer(hFileSlow, dwLow, &dwHigh, FILE_BEGIN);
  418.  
  419.         if (dwErrorRet = GetLastError())
  420.             throw dwErrorRet;
  421.  
  422.         hFOut = hFileSlow;
  423.     }
  424.  
  425.     if (lReadPointer + len > lBufferSize) {
  426.         long fraction;
  427.  
  428.         // split write
  429.  
  430.         fraction = lBufferSize - lReadPointer;
  431.  
  432.         BackgroundWrite(hFOut, lReadPointer, fraction);
  433.         BackgroundWrite(hFOut, 0, len - fraction);
  434.  
  435.         lReadPointer = len-fraction;
  436.  
  437.     } else {
  438.  
  439.         BackgroundWrite(hFOut, lReadPointer, len);
  440.         lReadPointer += len;
  441.  
  442.         if (lReadPointer >= lBufferSize)
  443.             lReadPointer -= lBufferSize;
  444.  
  445.     }
  446.  
  447.     // Atomically update data point and signal write thread
  448.  
  449.     __asm mov eax,this
  450.     __asm mov ebx,len
  451.     __asm lock sub [eax]FastWriteStream.lDataPoint,ebx
  452.  
  453.     SetEvent(hEventOkWrite);
  454.  
  455.     return true;
  456. }
  457.  
  458. void FastWriteStream::BackgroundThread() {
  459.     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
  460.  
  461.     while(!fDie) {
  462.         if (!BackgroundCheck())
  463.             WaitForSingleObject(hEventOkRead, INFINITE);
  464.     }
  465. }
  466.